import java.awt.Point;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

public class InfoAnalysis 
{
	private int maxSize;
	private int interval;
	private ArrayList<Long> timeSet = new ArrayList<Long>(maxSize);
	private int[] spotCheck = new int[maxSize*2];
	private Controller control;

	public InfoAnalysis(Controller myControl)
	{
		control=myControl;
	}


	public void generateDataSet(ArrayList<String> optionsPicked)
	{
		if (optionsPicked.get(0).equals("Data Structures"))
		{
			calledMethod(optionsPicked.get(1), optionsPicked.get(2));
		}
		if(optionsPicked.get(0).equals("Search Algorithms")){
			calledMethod("SearchingandSortingMethods", optionsPicked.get(1));
		}
		if(optionsPicked.get(0).equals("Sort Algorithms")){
			calledMethod("SearchingandSortingMethods", optionsPicked.get(1));
		}
		//text-to-code
		//then generates the data for the user's chosen method, then adds it to dataSets
	}

	private void calledMethod(String className, String methodName)
	{
		ArrayList<String> methodInfo=seperateParameters(methodName);
		try {
			Class cls = Class.forName(className);
			Object obj = cls.newInstance();
			Method method;
			if (methodInfo.size()==1)
			{
				
				method=cls.getMethod(methodInfo.get(0));
				testData(method, obj);
			}
			else if (true)
			{
				Integer param1=new Integer(getWhatToAdd());
				Integer param2=new Integer(getWhatToAdd());
				if (methodInfo.size()==2)
				{
					if (methodInfo.get(0).contains("get") && !className.contains("Hash"))
					{
						method=cls.getMethod(methodInfo.get(0), int.class);
					}
					else
						method=cls.getMethod(methodInfo.get(0), Object.class);
					testData(method, obj, param1);
				}
				else
				{
					if (methodInfo.get(0).contains("binary"))
					{
						method=cls.getMethod(methodInfo.get(0), ArrayList.class, Object.class);
					}
					else if (methodInfo.get(0).contains("add") || methodInfo.get(0).contains("set"))
					{
						method=cls.getMethod(methodInfo.get(0), int.class, Object.class);
					}
					else
						method=cls.getMethod(methodInfo.get(0), Object.class, Object.class);
					testData(method, obj, param1, param2);
				}
			}
			control.addDataSet(timeSet);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public ArrayList<String> seperateParameters(String methodName)
	{
		ArrayList<String> methodInfo=new ArrayList<String>();
		int parameters=0;
		int startParam=methodName.indexOf('(');
		methodInfo.add(methodName.substring(0, startParam));
		String params=methodName.substring(startParam+1,methodName.length()-1);
		while(params.length()>=1)
		{
			int nextComma=params.indexOf(',');
			String nextParams="";
			int startIndex=2;
			if (nextComma<=0)
			{
				//nextComma=params.length()-1;
				nextComma=1;
				startIndex=0;
			}
			else
			{
				nextParams=params.substring(nextComma,params.length());
			}
			String toAdd=params.substring(startIndex,nextComma);
			methodInfo.add(toAdd);
			params=nextParams;
		}
		return methodInfo;
	}

	private void testData(Method method, Object obj)
	{

		try {
			int max = maxSize;

			for(int start = interval; start <= max; start+=interval){
				for(int i = 0; i < interval; i++){
					if(obj instanceof Map)((Map) obj).put(getWhatToAdd(), getWhatToAdd());
					else ((Collection) obj).add(getWhatToAdd());
				}
				long startTime=System.nanoTime();
				method.invoke(obj);
				long endTime=System.nanoTime();
				long timeTaken = endTime - startTime;
				timeSet.add((Long)timeTaken);

			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void testData(Method method, Object obj, Object param1)
	{
		if (method.getName().contains("get"))
		{
			try {
				int max = maxSize;
				for(int start = interval; start <= max; start+=interval)
				{
					for(int i = 0; i < interval; i++){
						if(obj instanceof Map)
							((Map) obj).put(new Integer(getWhatToAdd()), new Integer(getWhatToAdd()));
						else 
							((Collection) obj).add(getWhatToAdd());
					}
					param1=(int)(Math.random()*start);
					long startTime=System.nanoTime();
					method.invoke(obj, param1);
					long endTime = System.nanoTime();
					long timeTaken = endTime - startTime;
					timeSet.add((Long)timeTaken);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return;
		}
		else
		{
		try {
			int max = maxSize;
			for(int start = interval; start <= max; start+=interval)
			{
				for(int i = 0; i < interval; i++){
					if(obj instanceof Map)
						((Map) obj).put(new Integer(getWhatToAdd()), new Integer(getWhatToAdd()));
					else 
						((Collection) obj).add(getWhatToAdd());
				}
				long startTime=System.nanoTime();
				method.invoke(obj, param1);
				long endTime = System.nanoTime();
				long timeTaken = endTime - startTime;
				timeSet.add((Long)timeTaken);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
	}

	private void testData(Method method, Object obj, Object param1, Object param2)
	{
		if(method.getName().contains("binary"))
		{
			try
			{
				int max = maxSize;
				for(int start = interval; start <= max; start+=interval)
				{
					ArrayList<Integer> ints = new ArrayList<Integer>(interval);
					for (int i=0;i<start;i++)
					{
						ints.add(i);
					}
					int key = ints.get((int)(Math.random()*start));
					long startTime=System.nanoTime();
					SearchingandSortingMethods.binarySearch(ints,new Integer(key));
					long endTime = System.nanoTime();
					long timeTaken = endTime - startTime;
					timeSet.add((Long)timeTaken);
				}
			}
			catch(Exception e)
			{
				
			}
		}
		else if(method.getName().contains("add") || method.getName().contains("set"))
		{
			try {
				int max = maxSize;
				for(int start = interval; start <= max; start+=interval){
					for(int i = 0; i < interval; i++){
						if(obj instanceof Map)((Map) obj).put(new Integer(getWhatToAdd()), new Integer(getWhatToAdd()));
						else ((Collection) obj).add(getWhatToAdd());
					}
					param1=(int)(Math.random()*start);
					long startTime = System.nanoTime();
					method.invoke(obj, param1, param2);
					long endTime = System.nanoTime();
					long timeTaken = endTime - startTime;
					timeSet.add((Long)timeTaken);

				}
			}
			catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else
		{
			try {
				int max = maxSize;
				for(int start = interval; start <= max; start+=interval){
					for(int i = 0; i < interval; i++){
						if(obj instanceof Map)((Map) obj).put(new Integer(getWhatToAdd()), new Integer(getWhatToAdd()));
						else ((Collection) obj).add(getWhatToAdd());
					}
					long startTime = System.nanoTime();
					method.invoke(obj, param1, param2);
					long endTime = System.nanoTime();
					long timeTaken = endTime - startTime;
					timeSet.add((Long)timeTaken);

				}
			}
			catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public int getWhatToAdd()
	{
		int whatToAdd = (int)(Math.random()*maxSize*2);
		while(spotCheck[whatToAdd] == 1)
			whatToAdd = (int)(Math.random()*maxSize*2);
		spotCheck[whatToAdd] = 1;
		return whatToAdd;
	}

	public void setMaxSize(int n){
		maxSize = n;
		spotCheck = new int[maxSize*2];
	}

	public void setInterval(int n){
		//interval = n;
		interval=1;
	}

	public Object[] getInfo(){
		Object[] whatToReturn = new Object[3];
		whatToReturn[0] = timeSet;
		whatToReturn[1] = maxSize;
		whatToReturn[2] = interval;
		return whatToReturn;
	}
}
